<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>单向数据流props</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
</head>

<body>
<fieldset>
    <legend>静态props和动态props</legend>
    <div id="app">
        <input type="text">
        <my-component msg="父组件内容变了我也不变"></my-component>
    </div>
    <div id="app1">
        <input type="text" v-model='value'>
        <my-component1 v-bind:msg='value'></my-component1>
    </div>
    <template id="t">
        <span>
            <span>静态props：{{msg}}</span>
        </span>
    </template>
    <template id="t1">
        <span>
            <span>动态props：{{msg}}</span>
        </span>
    </template>
    <p>1、不使用v-bind时，父组件向子组件传递的数据是静态的</p>
    <p>2、父组件：v-model='value'</p>
    <p>3、子组件：v-bind:msg='value'</p>
    <p>4、父组件通过v-model绑定了自己input的value，子组件通过v-bind绑定自己的msg等于父组件的value</p>
    <p>5、这样子组件就可以动态接受父组件的内容</p>
    <hr>
    <div id="app2">
        父组件内容：[1,2,3]
        <my-component2 msg='[1,2,3]'></my-component2>
        <my-component2 :msg='[1,2,3]'></my-component2>
    </div>
    <template id='t2'>
        <div>length：{{msg.length}}</div>
    </template>
    <p>1、直接向子组件传递数据：my-message='[1,2,3]</p>
    <p>2、通过v-bind向子组件传递数据:my-message='[1,2,3]'</p>
    <p>3、可以发现相同的内容是否使用v-bind，解析后内容长度是不同的</p>
    <p>4、直接传递数据会当作字符串解析，使用v-bind后vue会自动识别。本例子父组件内容是一个数组，加了v-bind后会当作数组</p>
</fieldset>
<fieldset>
    <legend>props其他数据类型传递和数据验证</legend>
    <div id="app3">
        <my-component3 v-bind:son_obj='father_obj' v-bind:arry='arry' v-bind:number='number'
                       v-bind:string='string' :boolean="boolean" :string_numb="string_numb"
                       v-bind:custom_fn="custom_fn">

        </my-component3>
    </div>
    <template id="t3">
        <div>
            <p>传递对象：{{son_obj.name}}、{{son_obj.age}}</p>
            <p>传递数组：{{arry}}</p>
            <p>传递数值：{{number}}</p>
            <p>传递字符：{{string}}</p>
            <p>传递布尔值：{{boolean}}</p>
            <p>只能字符串或数值：{{string_numb}}</p>
            <p>父组件不传值函数默认返回方式：{{fn_value}}</p>
            <p>自定义验证函数：{{custom_fn}}</p>
        </div>
    </template>
    <p>1、一般组件传递数据时都建议进行props数据验证</p>
    <p>2、数据验证时props就需要使用对象写法</p>
    <pre>
        <xmp>
            <my-component3 v-bind:son_obj='father_obj' v-bind:arry='arry' v-bind:number='number'
                           v-bind:string='string' :boolean="boolean" :string_numb="string_numb"
                           v-bind:custom_fn="custom_fn">
            </my-component3>
        </xmp>
            data: {
                father_obj: {
                    name: '张三',
                    age: '14'
                },
                arry: [1, 2],
                number: 123,
                //如果将number改为null，控制台报错，因为子组件props数据验证只接收number类型而且必修传不能为空
                string: '字符串',
                boolean: true,
                //如果将boolean的值改为非布尔值，控制台报错，因为子组件props数据验证，只接收布尔值
                string_numb: 123123
                //如果将string_numb的值改为布尔值，控制台报错，因为子组件props数据验证只接收字符串或数值类型
                }
            props: {
                son_obj: Object,
                //只接收数组，而且必须函数来返回
                arry: {
                    type: Array,
                    default: function () {
                        return []
                        }
                    },
                //只接收number类型，而且必须传
                number: {
                        type: Number,
                        required:true
                    },
                string: {
                        type: String,
                    },
                //对象写法，type设定类型为布尔值，default设定默认值为true，如果父组件传进来的不是布尔值控制台就会报错
                boolean: {
                        type: Boolean,
                        default: true
                    },
                //只接收字符串或数值类型的值
                string_numb: [String, Number],

                //如果是数组类型且父组件没给子组件传值(没有v-bind:fn_value="fn_value")，则默认通过函数返回一个值
                fn_value: {
                    type: Array,
                    default: function () {
                        return ['父组件没传值','我是函数默认返回值'];
                    },
                //自定义验证函数，custom_fn的值如小于10控制台会抛出错误
                custom_fn:{
                    validator:function (value) {//函数接收的value就是父组件custom_fn的值
                            return value>10
                        }
                    }
            }
    </pre>
</fieldset>
<fieldset>
    <legend>单项数据流</legend>
    <div id="app4">
        <label>
            我能改变子组件宽度<input type="text" v-model="father_width">
        </label>
        <my-component4 :son_width="father_width"></my-component4>
    </div>
    <template id="t4">
        <div :style="style">父组件能改变我的宽度</div>
    </template>
    <div id="app5">
        <label>
            我是son的父组件：
            <input type="text" v-model="father_valve">
        </label>
        <my-component5 :sonvalue1="father_valve"></my-component5>
        <my-component6 :sonvalue2="father_valve"></my-component6>
    </div>
    <template id="t5">
        <div>
            <span>son：没使用变量：{{sonvalue1}}</span>
            <input type="text" v-model="sonvalue1">
            <ul>
                <li>子组件没使用局部变量保存来自父组件的数据</li>
                <li>修改input的内容，会导致从父组件接收到的数据跟着变</li>
            </ul>
        </div>
    </template>
    <template id="t6">
        <div>
            <span>son：有使用变量：{{sonvalue2}}</span>
            <input type="text" v-model="new_sonvalue2">
            <ul>
                <li>子组件使用了局部变量保存来自父组件的数据</li>
                <li>修改input的内容，不会导致从父组件接收到的数据跟着变</li>
                <li>因为input的value绑定了子组件的局部变量new_sonvalue2: this.sonvalue2</li>
            </ul>
        </div>
    </template>
    <p>*** props传递数据是单向的，也就是父组件数据变化时会传递给子组件，但是反过来不行</p>
    <p>*** 目的 :是尽可能将父子组件解耦，避免子组件无意中修改了父组件的状态。</p>
    <p>*** 应用场景: 业务中会经常遇到两种需要改变 prop 的情况</p>
    <p>1、父组件传递的数据作为需要被转变的原始值传入。这种情况，子组件使用计算属性就可以</p>
    <p>2、父组件传递初始值进来，子组件将它作为初始值保存起来，在自己的作用域 下可以随意使用和修改</p>


</fieldset>
<script>
    var app = new Vue({
        el: '#app',
        components: {
            'my-component': {
                props: ['msg'],
                template: '#t'
            }
        }
    });
    var app1 = new Vue({
        el: '#app1',
        data: {
            value: '112wwww'
        },
        components: {
            'my-component1': {
                props: ['msg'],
                template: '#t1'
            }
        }
    });
    var app2 = new Vue({
        el: '#app2',
        components: {
            'my-component2': {
                props: ['msg'],
                template: '#t2'
            }
        }
    });
    //////////////////////////静态props和动态props//////////////////////////////
    var app3 = new Vue({
        el: '#app3',
        data: {
            father_obj: {
                name: '张三',
                age: '14'
            },
            arry: [1, 2],
            number: 123,
            string: '字符串',
            boolean: true,
            string_numb: 123123,
            fn_value:'',
            custom_fn:85
        },
        components: {
            'my-component3': {
                props: {
                    son_obj: Object,
                    arry: {
                        type: Array,
                    },
                    number: {
                        type: Number,
                        required: true
                    },
                    string: {
                        type: String,
                    },
                    boolean: {
                        type: Boolean,
                        default: true
                    },
                    string_numb: [String, Number],
                    fn_value: {
                        type: Array,
                        default: function () {
                            return ['父组件没传值','我是函数默认返回值'];
                        }
                    },
                    custom_fn:{
                        validator:function (value) {
                            return value>10
                        }
                    }
                },
                template: '#t3'
            }
        }
    });
    //////////////////////////对象方式传递及数据验证//////////////////////////////
    let app4 = new Vue({
        el: '#app4',
        data: {
            father_width: 20
        },
        components: {
            'my-component4': {
                props: ['son_width'],
                template: '#t4',
                computed: {
                    style: function () {
                        return {
                            width: this.son_width + 'px',
                            background: 'red'
                        }
                    }
                }
            }
        }
    });
    let app5 = new Vue({
        el: '#app5',
        data: {
            father_valve: 'hi'
        },
        components: {
            'my-component5': {
                template: '#t5',
                props: ['sonvalue1'],
            },
            'my-component6': {
                template: '#t6',
                props: ['sonvalue2'],
                data: function () {
                    return {
                        new_sonvalue2: this.sonvalue2
                    }
                }
            }
        }
    })

    /*
    * 关系理解： props向下传递，事件向上传递。父组件通过 props 给子组件下发数据，子组件通过事件给父组件发送消息
    * 数据说明：props组件声明后需要从父组件接收数据， props 的值可以是两种， 一种是字符串数组，一种是对象。
    * 在props中定义的属性，都可以在组件中直接使用
    * props数据和组件data中数据区别是，props来自父级，而组件内data的数据是组件自己的数据
    * */
</script>
</body>

</html>
